package com.hardydou.common.result;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;

import java.util.Collection;
import java.util.Map;

/**
 * @author ：hardy
 * @description：整合Result、Code的断言
 * @date ：Created in 2020/6/29
 * @Copyright ：Hardydou.com
 */
public class ResultAssert {


    /**
     * 断言是否为真，如果为 {@code false} 抛出 {@code NoConditionException} 异常<br>
     *
     * @param expression 布尔值
     * @param code
     * @throws NoConditionException if expression is {@code false}
     */
    public static void isTrue(boolean expression, Code code) throws NoConditionException {
        if (false == expression) {
            throw new NoConditionException(code);
        }
    }

    /**
     * 断言是否为真，如果为 {@code false} 抛出 {@code NoConditionException} 异常<br>
     *
     * @param expression 布尔值
     * @throws NoConditionException if expression is {@code false}
     */
    public static void isTrue(boolean expression) throws NoConditionException {
        isTrue(expression, Code.FAILURE);
    }

    /**
     * 断言是否为假，如果为 {@code true} 抛出 {@code NoConditionException} 异常<br>
     *
     * @param expression 布尔值
     * @param code       预言失败提示码
     * @throws NoConditionException if expression is {@code false}
     */
    public static void isFalse(boolean expression, Code code) throws NoConditionException {
        if (expression) {
            throw new NoConditionException(code);
        }
    }

    /**
     * 断言是否为假，如果为 {@code true} 抛出 {@code NoConditionException} 异常<br>
     *
     * <pre class="code">
     * Assert.isFalse(i &lt; 0);
     * </pre>
     *
     * @param expression 布尔值
     * @throws NoConditionException if expression is {@code false}
     */
    public static void isFalse(boolean expression) throws NoConditionException {
        isFalse(expression, Code.FAILURE);
    }

    /**
     * 断言对象是否为{@code null} ，如果不为{@code null} 抛出{@link NoConditionException} 异常
     *
     * <pre class="code">
     * Assert.isNull(value, "The value must be null");
     * </pre>
     *
     * @param object 被检查的对象
     * @param code   参数列表
     * @throws NoConditionException if the object is not {@code null}
     */
    public static void isNull(Object object, Code code) throws NoConditionException {
        if (object != null) {
            throw new NoConditionException(code);
        }
    }

    /**
     * 断言对象是否为{@code null} ，如果不为{@code null} 抛出{@link NoConditionException} 异常
     *
     * @param object 被检查对象
     * @throws NoConditionException if the object is not {@code null}
     */
    public static void isNull(Object object) throws NoConditionException {
        isNull(object, Code.FAILURE);
    }

    // ----------------------------------------------------------------------------------------------------------- Check not null

    /**
     * 断言对象是否不为{@code null} ，如果为{@code null} 抛出{@link NoConditionException} 异常 Assert that an object is not {@code null} .
     *
     * @param <T>    被检查对象泛型类型
     * @param object 被检查对象
     * @param code   参数
     * @return 被检查后的对象
     * @throws NoConditionException if the object is {@code null}
     */
    public static <T> T notNull(T object, Code code) throws NoConditionException {
        if (object == null) {
            throw new NoConditionException(code);
        }
        return object;
    }

    /**
     * 断言对象是否不为{@code null} ，如果为{@code null} 抛出{@link NoConditionException} 异常
     *
     * @param <T>    被检查对象类型
     * @param object 被检查对象
     * @return 非空对象
     * @throws NoConditionException if the object is {@code null}
     */
    public static <T> T notNull(T object) throws NoConditionException {
        return notNull(object, Code.HTTP_NOT_FOUND);
    }

    // ----------------------------------------------------------------------------------------------------------- Check empty

    /**
     * 检查给定字符串是否为空，为空抛出 {@link NoConditionException}
     *
     * <pre class="code">
     * Assert.notEmpty(name, "Name must not be empty");
     * </pre>
     *
     * @param <T>  字符串类型
     * @param text 被检查字符串
     * @param code 参数
     * @return 非空字符串
     * @throws NoConditionException 被检查字符串为空
     * @see StrUtil#isNotEmpty(CharSequence)
     */
    public static <T extends CharSequence> T notEmpty(T text, Code code) throws NoConditionException {
        if (StrUtil.isEmpty(text)) {
            throw new NoConditionException(code);
        }
        return text;
    }

    /**
     * 检查给定字符串是否为空，为空抛出 {@link NoConditionException}
     *
     * @param <T>  字符串类型
     * @param text 被检查字符串
     * @return 被检查的字符串
     * @throws NoConditionException 被检查字符串为空
     * @see StrUtil#isNotEmpty(CharSequence)
     */
    public static <T extends CharSequence> T notEmpty(T text) throws NoConditionException {
        return notEmpty(text, Code.FAILURE);
    }

    /**
     * 检查给定字符串是否为空白（null、空串或只包含空白符），为空抛出 {@link NoConditionException}
     *
     * @param <T>  字符串类型
     * @param text 被检查字符串
     * @param code 参数
     * @return 非空字符串
     * @throws NoConditionException 被检查字符串为空白
     * @see StrUtil#isNotBlank(CharSequence)
     */
    public static <T extends CharSequence> T notBlank(T text, Code code) throws NoConditionException {
        if (StrUtil.isBlank(text)) {
            throw new NoConditionException(code);
        }
        return text;
    }

    /**
     * 检查给定字符串是否为空白（null、空串或只包含空白符），为空抛出 {@link NoConditionException}
     *
     * <pre class="code">
     * Assert.notBlank(name, "Name must not be blank");
     * </pre>
     *
     * @param <T>  字符串类型
     * @param text 被检查字符串
     * @return 非空字符串
     * @throws NoConditionException 被检查字符串为空白
     * @see StrUtil#isNotBlank(CharSequence)
     */
    public static <T extends CharSequence> T notBlank(T text) throws NoConditionException {
        return notBlank(text, Code.FAILURE);
    }

    /**
     * 断言给定字符串是否不被另一个字符串包含（即是否为子串）
     *
     * <pre class="code">
     * Assert.doesNotContain(name, "rod", "Name must not contain 'rod'");
     * </pre>
     *
     * @param textToSearch 被搜索的字符串
     * @param substring    被检查的子串
     * @param code         参数列表
     * @return 被检查的子串
     * @throws NoConditionException 非子串抛出异常
     */
    public static String notContain(String textToSearch, String substring, Code code) throws NoConditionException {
        if (StrUtil.isNotEmpty(textToSearch) && StrUtil.isNotEmpty(substring) && textToSearch.contains(substring)) {
            throw new NoConditionException(code);
        }
        return substring;
    }

    /**
     * 断言给定字符串是否不被另一个字符串包含（即是否为子串）
     *
     * <pre class="code">
     * Assert.doesNotContain(name, "rod", "Name must not contain 'rod'");
     * </pre>
     *
     * @param textToSearch 被搜索的字符串
     * @param substring    被检查的子串
     * @return 被检查的子串
     * @throws NoConditionException 非子串抛出异常
     */
    public static String notContain(String textToSearch, String substring) throws NoConditionException {
        return notContain(textToSearch, substring, Code.FAILURE);
    }

    /**
     * 断言给定数组是否包含元素，数组必须不为 {@code null} 且至少包含一个元素
     *
     * <pre class="code">
     * Assert.notEmpty(array, "The array must have elements");
     * </pre>
     *
     * @param array 被检查的数组
     * @param code  参数列表
     * @return 被检查的数组
     * @throws NoConditionException if the object array is {@code null} or has no elements
     */
    public static Object[] notEmpty(Object[] array, Code code) throws NoConditionException {
        if (ArrayUtil.isEmpty(array)) {
            throw new NoConditionException(code);
        }
        return array;
    }


    /**
     * 断言给定数组是否不包含{@code null}元素，如果数组为空或 {@code null}将被认为不包含
     *
     * <pre class="code">
     * Assert.noNullElements(array, "The array must have non-null elements");
     * </pre>
     *
     * @param <T>   数组元素类型
     * @param array 被检查的数组
     * @param code  参数列表
     * @return 被检查的数组
     * @throws NoConditionException if the object array contains a {@code null} element
     */
    public static <T> T[] noNullElements(T[] array, Code code) throws NoConditionException {
        if (ArrayUtil.hasNull(array)) {
            throw new NoConditionException(code);
        }
        return array;
    }

    /**
     * 断言给定集合非空
     *
     * <pre class="code">
     * Assert.notEmpty(collection, "Collection must have elements");
     * </pre>
     *
     * @param <T>        集合元素类型
     * @param collection 被检查的集合
     * @param code       参数列表
     * @return 非空集合
     * @throws NoConditionException if the collection is {@code null} or has no elements
     */
    public static <T> Collection<T> notEmpty(Collection<T> collection, Code code) throws NoConditionException {
        if (CollectionUtil.isEmpty(collection)) {
            throw new NoConditionException(code);
        }
        return collection;
    }


    /**
     * 断言给定Map非空
     *
     * <pre class="code">
     * Assert.notEmpty(map, "Map must have entries");
     * </pre>
     *
     * @param <K>  Key类型
     * @param <V>  Value类型
     * @param map  被检查的Map
     * @param code 参数列表
     * @return 被检查的Map
     * @throws NoConditionException if the map is {@code null} or has no entries
     */
    public static <K, V> Map<K, V> notEmpty(Map<K, V> map, Code code) throws NoConditionException {
        if (CollectionUtil.isEmpty(map)) {
            throw new NoConditionException(code);
        }
        return map;
    }


    /**
     * 断言给定对象是否是给定类的实例
     *
     * <pre class="code">
     * Assert.instanceOf(Foo.class, foo);
     * </pre>
     *
     * @param <T>  被检查对象泛型类型
     * @param type 被检查对象匹配的类型
     * @param obj  被检查对象
     * @param code 参数列表
     * @return 被检查对象
     * @throws NoConditionException if the object is not an instance of clazz
     * @see Class#isInstance(Object)
     */
    public static <T> T isInstanceOf(Class<?> type, T obj, Code code) throws NoConditionException {
        notNull(type, code);
        if (false == type.isInstance(obj)) {
            throw new NoConditionException(code);
        }
        return obj;
    }


    /**
     * 断言 {@code superType.isAssignableFrom(subType)} 是否为 {@code true}.
     *
     * <pre class="code">
     * Assert.isAssignable(Number.class, myClass);
     * </pre>
     *
     * @param superType 需要检查的父类或接口
     * @param subType   需要检查的子类
     * @param code      参数列表
     * @throws NoConditionException 如果子类非继承父类，抛出此异常
     */
    public static void isAssignable(Class<?> superType, Class<?> subType, Code code) throws NoConditionException {
        notNull(superType, code);
        if (subType == null || !superType.isAssignableFrom(subType)) {
            throw new NoConditionException(code);
        }
    }


}
